package q2d.client.renderers;

/** GameRenderer:
 * Klasa renderujaca widok gry.
 */
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.io.Console;

import q2d.client.worlds.GameWorld;
import q2d.common.models.Map;
import q2d.common.models.TileTypes;
import q2d.common.models.units.Bullet;
import q2d.common.models.units.Message;
import q2d.common.models.units.Player;
import q2d.common.models.units.PlayerHit;
import q2d.common.models.units.Smoke;
import q2d.common.models.units.WallHit;


public class GameRenderer extends Renderer {

	// wymiary obszaru renderowania gry
	private final int VIEWPORT_WIDTH = 1100;
	private final int VIEWPORT_HEIGHT = 600;
	private int halfOftilesPerViewportWidth;
	private int halfOftilesPerViewportHeight;

	private Font gameFont = new Font("Tahoma",Font.BOLD ,10);

	private String debugString = "debug";// TODO wywalic to
	private GameWorld gameWorld;

	private Image backgroundImage;
	private Image maskL;
	private Image maskR;
	private Image maskT;
	private Image maskB;

	/**
	 * Konstruktor.
	 * @param game_width
	 * @param game_height
	 * @param game_world referencja obiektu GameWorld ktory ma byc renderowany
	 */
	public GameRenderer(int game_width, int game_height, GameWorld game_world){
		super(game_width, game_height);
		gameWorld = game_world;

		halfOftilesPerViewportWidth = (int)((VIEWPORT_WIDTH/GFactory.TILE_WIDTH)/2);
		halfOftilesPerViewportHeight = (int)((VIEWPORT_HEIGHT/GFactory.TILE_HEIGHT)/2);

		backgroundImage = image(GFactory.IMG_BACKGROUND);
		maskL = image(GFactory.IMG_MASK_L);
		maskR = image(GFactory.IMG_MASK_R);
		maskT = image(GFactory.IMG_MASK_T);
		maskB = image(GFactory.IMG_MASK_B);
	}


	@Override
	public void draw(Graphics2D g) {
		int i;

		g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		g.setFont(gameFont);
		g.setColor(Color.BLACK);

		// rysowanie tla
		g.drawImage(backgroundImage, 0, 0, null);

		// obliczenie offsetu dla polozen obiektow - centrowanie widoku
		int offsetX = screenCenterX-gameWorld.thisPlayer.x;
		int offsetY = screenCenterY-gameWorld.thisPlayer.y;

		offsetX = Math.min(offsetX, 144);
		offsetX = Math.max(offsetX, -100);
		offsetY = Math.min(offsetY, 154);
		offsetY = Math.max(offsetY, -584);

		debugString = String.valueOf(offsetY);
 
		// obliczenie zakresu kafelkow do wyrenderowania
		int playerTileX = (int)Math.floor(gameWorld.thisPlayer.x / GFactory.TILE_WIDTH);
		int playerTileY = (int)Math.floor(gameWorld.thisPlayer.y / GFactory.TILE_HEIGHT);
		int xBegin = Math.max(0, playerTileX - halfOftilesPerViewportWidth);
		int xEnd = Math.min(Map.COLS, playerTileX + halfOftilesPerViewportWidth);
		int yBegin = Math.max(0, playerTileY - halfOftilesPerViewportHeight);
		int yEnd = Math.min(Map.ROWS, playerTileY + halfOftilesPerViewportHeight);
		
		//ogranicza obszar gdy gracz znajduje sie w rogach ekranu
		if(yBegin ==0) yEnd =12;
		if(xBegin == 0) xEnd = 22;
		if(yEnd == Map.ROWS) yBegin =Map.ROWS - 12;
		if(xEnd == Map.COLS) xBegin = Map.COLS -22;
		
		
		// rysowanie mapy
		for (int y=yBegin; y<yEnd; y++){
			for (int x=xBegin; x<xEnd; x++){
				// rozpoznanie kafelka
				switch (gameWorld.gameMap.getTile(x, y)){
					case TileTypes.FLOOR_1:
						g.drawImage(image(GFactory.IMG_FLOOR1), offsetX+x*GFactory.TILE_WIDTH, offsetY+y*GFactory.TILE_HEIGHT, null);
						break;
					case TileTypes.WALL_1:
						g.drawImage(image(GFactory.IMG_WALL1), offsetX+x*GFactory.TILE_WIDTH, offsetY+y*GFactory.TILE_HEIGHT, null);
						break;
				}
			}
		}


		// rysowanie FX'ow
		for (i=0; i<gameWorld.fxSmokesList.size(); i++){
			Smoke s = (Smoke)gameWorld.fxSmokesList.get(i);

			if (isVisible(s.x, s.y)){
				g.drawImage(image(s.anim.getImage()), offsetX+s.x, offsetY+s.y, null);
			}
		}
		// rysowanie odpryskow
		for (i=0; i<gameWorld.fxWallHitsList.size(); i++){
			WallHit s = (WallHit)gameWorld.fxWallHitsList.get(i);

			if (isVisible(s.x, s.y)){
				g.drawImage(image(GFactory.IMG_HITEXP), offsetX+s.x, offsetY+s.y, null);
			}
		}
		
		// rysowanie gracza tej instancji gry
		AffineTransform transform = new AffineTransform();
		Player p = gameWorld.thisPlayer;
		p.anim.update();
		transform.setToTranslation(offsetX+p.x, offsetY+p.y);
		gameWorld.thisPlayer.screenX = offsetX+p.x;
		gameWorld.thisPlayer.screenY = offsetY+p.y;
		transform.rotate(p.rotation, Player.BOUND_RADIUS, Player.BOUND_RADIUS);

		//if (p.team == Player.BLUE_TEAM){
		// wyciaga odpowiednia grafike gracza niezaleznie od druzyny
		g.drawImage(image(p.anim.getImage()), transform, null);

		g.drawString(p.nick, offsetX+p.x, offsetY+p.y + Player.BOUND_RADIUS*2);


		// rysowanie obcych graczy
		for (i=0; i<gameWorld.playersList.size(); i++){
			transform = new AffineTransform();
			p = gameWorld.playersList.get(i);
			
			if (isVisible(p.x, p.y)){
				transform.setToTranslation(offsetX+p.x, offsetY+p.y);
				transform.rotate(p.rotation, Player.BOUND_RADIUS, Player.BOUND_RADIUS);

				if (p.team == Player.BLUE_TEAM){
					g.drawImage(image(GFactory.IMG_PLAYER_BLUE_1), transform, null);
				} else {
					g.drawImage(image(GFactory.IMG_PLAYER_RED_1), transform, null);
				}

				// wypisuje nazwe gracza
				//System.out.println(p.nick);
				g.drawString(p.nick, offsetX+p.x, offsetY+p.y + Player.BOUND_RADIUS*2);

			}
		}


		// rysowanie pociskow obcych graczy
		for (i=0; i<gameWorld.othersBulletsList.size(); i++){
			Bullet b = gameWorld.othersBulletsList.get(i);
			transform = new AffineTransform();

			if (isVisible((int)b.x, (int)b.y)){
				transform.setToTranslation(offsetX+(int)b.x, offsetY+(int)b.y);
				transform.rotate(Math.atan2(b.vy, b.vx)+Math.PI/2, 2, 8);
				switch (b.type){
					case Bullet.TYPE_1:
						g.drawImage(image(GFactory.IMG_BULLET_1), transform, null);
						break;
				}
			}
		}


		// rysowanie punktow trafien
		for (i=0; i<gameWorld.fxPlayerHitList.size(); i++){
			PlayerHit ph = gameWorld.fxPlayerHitList.get(i);
			g.drawImage(image(GFactory.IMG_HIT), offsetX+(int)ph.x, offsetY+(int)ph.y, null);
		}


		// rysowanie flag
		if (isVisible(gameWorld.blueFlag.x, gameWorld.blueFlag.y)){
			g.drawImage(image(GFactory.IMG_BLUE_FLAG), offsetX+gameWorld.blueFlag.x, offsetY+gameWorld.blueFlag.y, null);
		}
		if (isVisible(gameWorld.redFlag.x, gameWorld.redFlag.y)){
			g.drawImage(image(GFactory.IMG_RED_FLAG), offsetX+gameWorld.redFlag.x, offsetY+gameWorld.redFlag.y, null);
		}


		// rysowanie maski
		g.drawImage(maskL, 0, 0, null);
		g.drawImage(maskR, 1120, 0, null);
		g.drawImage(maskT, 160, 0, null);
		g.drawImage(maskB, 160, 640, null);


		// rysowanie komunikatow
		for (i=0; i<gameWorld.fxMessageList.size(); i++){
			Message message = gameWorld.fxMessageList.get(i);
			if (isVisible(message.x, message.y)){
				g.drawImage(image(message.messageType), offsetX+message.x + 10, offsetY+message.y-30, null);
			}
		}
	}


	public void setDebugString(String debug_string){
		debugString = debug_string;
	}


	/**
	 * Informuje o tym, czy dana pozycja (x,y) jest aktualnie widoczna na ekranie
	 * @param x
	 * @param y
	 * @return
	 */
	private boolean isVisible(int x, int y){
		return (x < gameWorld.thisPlayer.x + VIEWPORT_WIDTH/2 && x > gameWorld.thisPlayer.x - VIEWPORT_WIDTH/2) && (y < gameWorld.thisPlayer.y + VIEWPORT_HEIGHT/2 && y > gameWorld.thisPlayer.y - VIEWPORT_HEIGHT/2);
	}
}
